package cn.good.yan.b4.a1;

import java.util.*;
import java.lang.reflect.Field;

/**
 * b4. 注解
 *   字段对比 工具类
 *
 * @author shengren.yan
 * @create 2022/10/26
 */
public class CompareUtils<T> {


    /**
     * 对象比较
     *
     * @param source 源数据对象
     * @param target 目标数据对象
     * @return 对应属性值的比较变化
     */
    public List<CompareNode> compare(T source, T target) {
        return compare(source, target, null);
    }


    /**
     * 对象比较
     *
     * @param source       源数据对象
     * @param target       目标数据对象
     * @param ignoreFields 忽略比较的字段
     * @return 对应属性值的比较变化
     */
    public List<CompareNode> compare(T source, T target, List<String> ignoreFields) {
        if (Objects.isNull(source) && Objects.isNull(target)) {
            return new ArrayList<>();
        }
        Map<String, CompareNode> sourceMap = this.getFiledValueMap(source);
        Map<String, CompareNode> targetMap = this.getFiledValueMap(target);
        return doCompare(sourceMap, targetMap, ignoreFields);
    }

    /**
     * 比对 - 返回出有更改的值集合
     *
     * @param sourceMap           源图
     * @param targetMap           目标地图
     * @param ignoreCompareFields 忽略比较字段
     * @return {@code List<CompareNode>}
     */
    private List<CompareNode> doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) {
        List<CompareNode> nodeList = new ArrayList<>();
        Set<String> keys = sourceMap.keySet();
        int current = 0;
        for (String key : keys) {
            current++;
            CompareNode sn = sourceMap.get(key);
            CompareNode tn = targetMap.get(key);
            if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) {
                continue;
            }
            String sv = Optional.ofNullable(sn.getFieldValue()).orElse("").toString();
            String tv = Optional.ofNullable(tn.getFieldValue()).orElse("").toString();
            // 只有两者属性值不一致时, 才显示变化情况
            if (!sv.equals(tv)) {
                CompareNode node = new CompareNode();
                node.setFieldKey(sn.getFieldKey());
                node.setFieldName(sn.getFieldName());
                node.setNewValue(sv);
                node.setOldValue(tv);
                nodeList.add(node);
            }
        }
        return nodeList;
    }

    /**
     * 反射出全都对对象属性
     *
     * @param t t
     * @return {@code Map<String, CompareNode>}
     */
    private Map<String, CompareNode> getFiledValueMap(T t) {
        if (Objects.isNull(t)) {
            return Collections.emptyMap();
        }
        Field[] fields = t.getClass().getDeclaredFields();
        if (fields.length == 0) {
            return Collections.emptyMap();
        }
        Map<String, CompareNode> map = new LinkedHashMap();
        for (Field field : fields) {
            Compare compareAnnotation = field.getAnnotation(Compare.class);
            if (Objects.isNull(compareAnnotation)) {
                continue;
            }
            field.setAccessible(true);
            try {
                CompareNode node = new CompareNode();
                node.setFieldKey(compareAnnotation.key());
                node.setFieldValue(field.get(t));
                node.setFieldName(compareAnnotation.value());
                map.put(field.getName(), node);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * 空对象
     *
     * @param targetMap    目标地图
     * @param ignoreFields 忽略字段
     */
    private void doEmpty(Map<String, CompareNode> targetMap, List<String> ignoreFields) {
        Collection<CompareNode> values = targetMap.values();
        int current = 0;
        for (CompareNode node : values) {
            current++;
            Object o = Optional.ofNullable(node.getFieldValue()).orElse("");
            if (Objects.nonNull(ignoreFields) && ignoreFields.contains(node.getFieldKey())) {
                continue;
            }
            if (o.toString().length() > 0) {
                System.out.println(node.getFieldName() + o);
            }
        }
    }

}
